home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / misc / volume14 / lc / part01 next >
Encoding:
Text File  |  1990-09-15  |  54.9 KB  |  2,080 lines

  1. Newsgroups: comp.sources.misc
  2. X-UNIX-From: ssbell!sparky!kent
  3. subject: v14i082: lc - Categorize and List Files In Columns, Part 1 of 2
  4. from: kent@sparky.IMD.Sterling.COM (Kent Landfield)
  5. Sender: allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc)
  6.  
  7. Posting-number: Volume 14, Issue 82
  8. Submitted-by: kent@sparky.IMD.Sterling.COM (Kent Landfield)
  9. Archive-name: lc/part01
  10.  
  11. [It's decidedly difficult to gain access to one's uunet mailbox when one's
  12. Internet access denies all knowledge of uunet.  One could wish OS upgrades
  13. went faster than taking an entire week.  ++bsa]
  14.  
  15. [Xenix warning:  this conflicts with /bin/lc.  I suggest a rename.  ++bsa]
  16.  
  17. #! /bin/sh
  18. # This is a shell archive.  Remove anything before this line, then feed it
  19. # into a shell via "sh file" or similar.  To overwrite existing files,
  20. # type "sh file -c".
  21. # The tool that generated this appeared in the comp.sources.unix newsgroup;
  22. # send mail to comp-sources-unix@uunet.uu.net if you want that tool.
  23. # If this archive is complete, you will see the following message at the end:
  24. #        "End of archive 1 (of 2)."
  25. # Contents:  README MANIFEST lc.c lc.mk qsort.c
  26. # Wrapped by kent@sparky on Fri Sep  7 16:10:04 1990
  27. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  28. if test -f 'README' -a "${1}" != "-c" ; then 
  29.   echo shar: Will not clobber existing file \"'README'\"
  30. else
  31. echo shar: Extracting \"'README'\" \(4940 characters\)
  32. sed "s/^X//" >'README' <<'END_OF_FILE'
  33. X
  34. X            LC
  35. X
  36. X        "@(#)README    1.3 9/7/90 Kent Landfield"
  37. X
  38. XThis directory contains the source to lc. lc is much like the ls 
  39. Xcommand except it separates the types of files into groups and then
  40. Xdisplays located file names to the user is a columnar fashion. 
  41. X
  42. Xlc was initially implemented by myself after I became accustomed
  43. Xto the lc command on a Mark Williams Coherent system I was running back
  44. Xin 1984.  When I started moving to different systems I found that I did
  45. Xnot like the lack of information, flexibility and the general display 
  46. Xthat ls provided.  I have found that I am not the only one that has 
  47. Xbecome feed up with ls and its jumbled output. All the people here use 
  48. Xlc much more than they use ls. It is a locally well known fact that when
  49. Xa new machine comes into the house, the first local software that is put 
  50. Xon the machine is lc.  It is far from perfect but it is better than ls on 
  51. Xa general use basis.
  52. X
  53. Xlc uses three different environment variables COLS, CDPATH, and LC
  54. Xallowing you to customize how you wish lc to perform on a default 
  55. Xbasis. COLS is used to switch the column width between 80 and 132 
  56. Xcolumn display.  LC is used to set lc specific options. The command 
  57. Xline options shown in the option list below are available to be used 
  58. Xin setting up how lc is to work for you. Options that you supply on 
  59. Xthe command line override options that are specified in the environment.
  60. Xlc uses CDPATH to locate files that are requested but are not in the 
  61. Xspecified location or within the local directory.
  62. X
  63. Xlc has incorporated minimal spell checking using slightly modified
  64. Xroutines found in the book, The UNIX Programming Environment, by 
  65. XBrian Kernighan and Rob Pike.
  66. X
  67. XThe following is a quick reference of options to lc. These options
  68. Xcan be specified in the Environment variable LC so that they are
  69. Xdone each time lc is executed.
  70. X
  71. X lc -- categorize files in a directory and list column-wise
  72. X
  73. X   Usage:       lc [ options ] [ directory ... ]
  74. X
  75. X   Options:
  76. X        -a      List dot files as well.
  77. X        -b      List block special files only
  78. X        -c      List character special files only
  79. X        -d      List directories only
  80. X        -D      Do not display singular files
  81. X        -e      Mark executable files with '*'
  82. X        -f      List regular files only
  83. X        -F      List fifo files only
  84. X        -1      List files one per line instead of in columns
  85. X        -r      Do not sort the file names before displaying.
  86. X        -m      List shared memory name space entry files only
  87. X        -M      List semaphore name space entry files only
  88. X        -S      List socket file only
  89. X        -s      List symbolic links only
  90. X        -L      Display symbolic links
  91. X        -l      Mark symbolic links with '@'
  92. X        -I      Suppress unresolved symbolic link messages.
  93. X  
  94. X   The "only" options can not be combined.
  95. X
  96. X   If there is no 'directory' specified, the current directory is used.
  97. X
  98. X   Not all options are supported on every system. (e.g. no symbolic links
  99. X   on your system ? Options -s, -L or -l won't be available..)
  100. X   If your system does not support shared memory name space entry files, 
  101. X   then you cannot use the -m option...
  102. X
  103. XHistory:
  104. X      Initially designed on an IBM-XT running Coherent in 1984.
  105. X      Ported to XENIX on an IBM-AT in 1984.
  106. X      Ported to System V on AT&T 3Bs in 1985.
  107. X      Ported to DEC Vax 11/750 running System V in 1986.
  108. X      Ported to BSD4.2 on a Sequent Balance 8000 in 1986.
  109. X      Jeff Minnig added the initial support for links.
  110. X      Ported to SunOS 4.0 on a Sun 3/60 in 1988.
  111. X      Rick Ohnemus did major surgery to remove static storage
  112. X      and *greatly* enhanced the link support. Thanks rick!
  113. X      Tested with Ultrix 3.0 & 3.1 on a DECstation 3100 in 1989.
  114. X      Tested with Ultrix 3.0 & 3.1 on a VAXstation 3500 in 1989.
  115. X      Ported to AIX 2.2 on an IBM RT.
  116. X      Tested with UTek on a Tektronix 4319 in 1989.
  117. X      Tested with IRIX System V on a Silicon Graphics Iris 4D/210GTX in 1989.
  118. X      Tested with AmigaDOS 1.3 on an Amiga 1000 in 1989.
  119. X      Tested with SunOS 4.0.3 on a Sparkstation 1 in 1989.
  120. X      Tested with UTek on a Tektronix XD8810 in 1989.
  121. X      Tested with AIX 3.+ on a Risc System/6000 in 1990.
  122. X
  123. XThe following is a todo list that may get done someday...
  124. X
  125. X1. Port to all flavors of unix on all platforms. Lofty goal but
  126. X   on-going... :-)
  127. X2. Need to modify lc so that one environment (or compile) option will
  128. X   only let you see files you own or have access to.
  129. X3. Add spell checking code to CDPATH usage
  130. X4. Allow combination of only options...
  131. X                                                        
  132. X                          *PLEASE!* 
  133. XIf you have a problem, there's someone else out there who either has
  134. Xhad or will have the same problem.  Please send all "lc" ideas, patches, 
  135. Xetc to 
  136. X
  137. XINTERNET: kent@sparky.IMD.Sterling.COM or UUCP: uunet!ssbell!sparky!kent
  138. X
  139. Xso that I can continue to improve the functionality and portability of lc.
  140. X
  141. X            -Kent+
  142. X
  143. X    
  144. END_OF_FILE
  145. if test 4940 -ne `wc -c <'README'`; then
  146.     echo shar: \"'README'\" unpacked with wrong size!
  147. fi
  148. # end of 'README'
  149. fi
  150. if test -f 'MANIFEST' -a "${1}" != "-c" ; then 
  151.   echo shar: Will not clobber existing file \"'MANIFEST'\"
  152. else
  153. echo shar: Extracting \"'MANIFEST'\" \(442 characters\)
  154. sed "s/^X//" >'MANIFEST' <<'END_OF_FILE'
  155. X   File Name        Archive #    Description
  156. X----------------------------------------------------------
  157. X README                     1    General information file.
  158. X MANIFEST                   1    This shipping list
  159. X lc.1                       2    lc manual page in nroff format.
  160. X lc.c                       1    Source to the lc command.
  161. X lc.mk                      1    Makefile for the lc command.
  162. X qsort.c                    1    BSD libc.a qsort source (optional)
  163. END_OF_FILE
  164. if test 442 -ne `wc -c <'MANIFEST'`; then
  165.     echo shar: \"'MANIFEST'\" unpacked with wrong size!
  166. fi
  167. # end of 'MANIFEST'
  168. fi
  169. if test -f 'lc.c' -a "${1}" != "-c" ; then 
  170.   echo shar: Will not clobber existing file \"'lc.c'\"
  171. else
  172. echo shar: Extracting \"'lc.c'\" \(35879 characters\)
  173. sed "s/^X//" >'lc.c' <<'END_OF_FILE'
  174. X/*
  175. X** This software is 
  176. X**
  177. X** Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 by Kent Landfield.
  178. X**
  179. X** Permission is hereby granted to copy, distribute or otherwise 
  180. X** use any part of this package as long as you do not try to make 
  181. X** money from it or pretend that you wrote it.  This copyright 
  182. X** notice must be maintained in any copy made.
  183. X**
  184. X** Use of this software constitutes acceptance for use in an AS IS 
  185. X** condition. There are NO warranties with regard to this software.  
  186. X** In no event shall the author be liable for any damages whatsoever 
  187. X** arising out of or in connection with the use or performance of this 
  188. X** software.  Any use of this software is at the user's own risk.
  189. X**
  190. X**  If you make modifications to this software that you feel 
  191. X**  increases it usefulness for the rest of the community, please 
  192. X**  email the changes, enhancements, bug fixes as well as any and 
  193. X**  all ideas to me. Thanks!
  194. X**
  195. X**              Kent Landfield
  196. X**              kent@sparky.IMD.Sterling.COM
  197. X**              sparky!kent
  198. X**
  199. X**  Subsystem:   lc - List Columnwise/Categories
  200. X**
  201. X**  Abstract :   lc -- categorize files in a directory and list columnwize
  202. X**
  203. X**  Usage:       lc [ options ] [ directory ... ]
  204. X**
  205. X**  Options:
  206. X**               -a      List dot files as well.
  207. X**               -b      List block special files only
  208. X**               -c      List character special files only
  209. X**               -d      List directories only
  210. X**               -D      Do not display singular files
  211. X**               -e      Mark executable files with '*'
  212. X**               -f      List regular files only
  213. X**               -F      List fifo files only
  214. X**               -1      List files one per line instead of in columns
  215. X**               -r      Do not sort the filenames before displaying.
  216. X**               -m      List shared memory name space entry files only
  217. X**               -M      List semaphore name space entry files only
  218. X**               -S      List socket file only
  219. X**               -s      List symbolic links only
  220. X**               -L      Display symbolic links
  221. X**               -l      Mark symbolic links with '@'
  222. X**               -I      Suppress unresolved symbolic link messages.
  223. X**
  224. X** The "only" options can not be combined.
  225. X** If there is no 'directory' specified, the current directory is used.
  226. X** Not all options are supported on every system. (e.g. no symbolic links
  227. X** on your system ? Options -s, -L or -l won't be available..)
  228. X**
  229. X**  History:
  230. X**      Initially designed on an IBM-XT running Coherent in 1984.
  231. X**      Ported to XENIX on an IBM-AT in 1984.
  232. X**      Ported to System V on AT&T 3Bs in 1985.
  233. X**      Ported to DEC Vax 11/750 running System V in 1986.
  234. X**      Ported to BSD4.2 on a Sequent Balance 8000 in 1986.
  235. X**      Jeff Minnig added the initial support for links. 
  236. X**      Ported to SunOS 4.0 on a Sun 3/60 in 1988.
  237. X**      Rick Ohnemus did major surgery to remove static storage
  238. X**      and *greatly* enhanced the link support. Thanks rick!
  239. X**      Tested with Ultrix 3.0 & 3.1 on a DECstation 3100 in 1989.
  240. X**      Tested with Ultrix 3.0 & 3.1 on a VAXstation 3500 in 1989.
  241. X**      Tested with UTek on a Tektronix 4319 in 1989.
  242. X**      Tested with IRIX System V on a Silicon Graphics Iris 4D/210GTX in 1989.
  243. X**      Tested with AmigaDOS 1.3 on an Amiga 1000 in 1989.
  244. X**      Tested with SunOS 4.0.3 on a Sparkstation 1 in 1989.
  245. X**      Tested with AIX 3.+ on a Risc System/6000 in 1990. 
  246. X**                                                               
  247. X*/
  248. X#ifndef lint
  249. Xstatic char *sccsid = "@(#)lc.c    1.23 9/7/90  Kent Landfield";
  250. X#endif
  251. X
  252. X#include <stdio.h>
  253. X#ifdef BSD
  254. X#  include <strings.h>
  255. X#  include <sys/param.h>
  256. X#else
  257. X#  include <string.h>
  258. X#  include <sys/types.h>
  259. X#endif
  260. X#include <sys/stat.h>
  261. X#ifdef POSIX
  262. X#  include <limits.h>
  263. X#  include <dirent.h>
  264. X#else
  265. X#  ifdef BSD
  266. X#    ifdef DIRECT
  267. X#      include <sys/dir.h>
  268. X#    else
  269. X#      include <dirent.h>
  270. X#    endif
  271. X#  else
  272. X#    include <sys/dir.h>
  273. X#  endif
  274. X#endif
  275. X
  276. X#ifndef NAME_MAX
  277. X#  ifdef BSD
  278. X#    define NAME_MAX    MAXNAMLEN
  279. X#  else
  280. X#    ifdef DIRSIZ
  281. X#      define NAME_MAX  DIRSIZ
  282. X#    else
  283. X#      define NAME_MAX  14
  284. X#    endif
  285. X#  endif
  286. X#endif
  287. X
  288. X#ifndef PATH_MAX
  289. X#  ifdef MAXPATHLEN
  290. X#    define PATH_MAX    MAXPATHLEN
  291. X#  else
  292. X#    ifdef MAXNAMLEN
  293. X#      define PATH_MAX  MAXNAMLEN
  294. X#    else
  295. X#      define PATH_MAX  255
  296. X#    endif
  297. X#  endif
  298. X#endif
  299. X
  300. X#define BUFSIZE         PATH_MAX
  301. X
  302. X#define NODES_PER_HUNK  256
  303. X
  304. X#define TRUE            1
  305. X#define FALSE           0
  306. X
  307. X#ifndef S_IXUSR
  308. X#  define S_IXUSR       S_IEXEC
  309. X#endif
  310. X
  311. X#ifndef S_IXGRP
  312. X#  define S_IXGRP       (S_IEXEC >> 3)
  313. X#endif
  314. X
  315. X#ifndef S_IXOTH
  316. X#  define S_IXOTH       (S_IEXEC >> 6)
  317. X#endif
  318. X
  319. X#define DIR_ONLY        1
  320. X#define FILE_ONLY       2
  321. X#ifdef S_IFCHR
  322. X#  define CHAR_ONLY     3
  323. X#endif
  324. X#ifdef S_IFBLK
  325. X#  define BLOCK_ONLY    4
  326. X#endif
  327. X#ifdef S_IFIFO
  328. X#  define FIFO_ONLY     5
  329. X#endif
  330. X#ifdef S_IFLNK
  331. X#  define LNK_ONLY      6
  332. X#endif
  333. X#ifdef S_IFSOCK
  334. X#  define SOCK_ONLY     7
  335. X#endif
  336. X#ifdef S_IFNAM
  337. X#  define SEM_ONLY      8
  338. X#  define SD_ONLY       9
  339. X#endif
  340. X
  341. X#ifdef BSD
  342. X#  define strrchr       rindex
  343. X#  define strchr        index
  344. X#endif
  345. X
  346. X
  347. Xstruct list {
  348. X    int num;
  349. X    int max;
  350. X    char **names;
  351. X#ifdef LENS
  352. X    int maxlen;
  353. X#endif
  354. X};
  355. X
  356. X#ifdef LENS
  357. X
  358. X#ifdef S_IFBLK
  359. Xstruct list Blks = { 0, 0, (char **) NULL, 0 };
  360. X#endif
  361. X
  362. X#ifdef S_IFCHR
  363. Xstruct list Chrs = { 0, 0, (char **) NULL, 0 };
  364. X#endif
  365. X
  366. Xstruct list Dirs = { 0, 0, (char **) NULL, 0 };
  367. Xstruct list Fls = { 0, 0, (char **) NULL, 0 };
  368. X
  369. X#ifdef S_IFIFO
  370. Xstruct list Fifos = { 0, 0, (char **) NULL, 0 };
  371. X#endif
  372. X
  373. X#ifdef S_IFLNK
  374. Xstruct list Lnks = { 0, 0, (char **) NULL, 0 };
  375. Xstruct list Lnksn = { 0, 0, (char **) NULL, 0 };
  376. X#endif
  377. X
  378. X#ifdef S_IFSOCK
  379. Xstruct list Socks = { 0, 0, (char **) NULL, 0 };
  380. X#endif
  381. X
  382. X#ifdef S_IFNAM
  383. Xstruct list Sds = { 0, 0, (char **) NULL, 0 };
  384. Xstruct list Sems = { 0, 0, (char **) NULL, 0 };
  385. X#endif
  386. X
  387. X#else   /* ifndef LENS */
  388. X
  389. X#ifdef S_IFBLK
  390. Xstruct list Blks = { 0, 0, (char **) NULL };
  391. X#endif
  392. X
  393. X#ifdef S_IFCHR
  394. Xstruct list Chrs = { 0, 0, (char **) NULL };
  395. X#endif
  396. X
  397. Xstruct list Dirs = { 0, 0, (char **) NULL };
  398. Xstruct list Fls = { 0, 0, (char **) NULL };
  399. X
  400. X#ifdef S_IFIFO
  401. Xstruct list Fifos = { 0, 0, (char **) NULL };
  402. X#endif
  403. X
  404. X#ifdef S_IFLNK
  405. Xstruct list Lnks = { 0, 0, (char **) NULL };
  406. Xstruct list Lnksn = { 0, 0, (char **) NULL };
  407. X#endif
  408. X
  409. X#ifdef S_IFSOCK
  410. Xstruct list Socks = { 0, 0, (char **) NULL };
  411. X#endif
  412. X
  413. X#ifdef S_IFNAM
  414. Xstruct list Sds = { 0, 0, (char **) NULL };
  415. Xstruct list Sems = { 0, 0, (char **) NULL };
  416. X#endif
  417. X
  418. X#endif /* LENS */
  419. X
  420. Xchar *Progname;
  421. X
  422. Xint Allfiles = FALSE;
  423. Xint Display_single = TRUE;
  424. Xint Executables = FALSE;
  425. Xint Ignore = FALSE;
  426. Xint Level = 0;
  427. Xint Maxlen = 0;
  428. Xint Only = FALSE;
  429. Xint Screen_width = 80;
  430. Xint Single = FALSE;
  431. Xint Sort_wanted = TRUE;
  432. X
  433. X#ifdef S_IFLNK
  434. Xint Current = 0;
  435. Xint Disp_links = FALSE;
  436. Xint Mark_links = FALSE;
  437. Xint lstat();
  438. Xint readlink();
  439. X#endif
  440. X
  441. X#ifndef _BSD
  442. X# ifdef BSD
  443. X    extern char *sprintf();
  444. X    extern int  exit();
  445. X    extern int  free();
  446. X    extern int  qsort();
  447. X# else
  448. X    extern int  sprintf();
  449. X    extern void exit();
  450. X    extern void free();
  451. X    extern void qsort();
  452. X# endif
  453. X extern int fprintf();
  454. X extern int printf();
  455. X extern int sscanf();
  456. X#endif
  457. X
  458. Xvoid lc();
  459. X
  460. Xextern char *getenv();
  461. Xextern char *malloc();
  462. Xextern char *realloc();
  463. Xextern int access();
  464. Xextern int fputs();
  465. Xextern int puts();
  466. Xextern int stat();
  467. X
  468. X/* S T R _ S A V  
  469. X *
  470. X *   str_sav() returns a pointer to a new string which is a dupli-
  471. X *   cate  of the string pointed to by s.  The space for the new
  472. X *   string is obtained using malloc(3).  If the new string  can-
  473. X *   not be created, the process prints an error message on stderr
  474. X *   and terminates.
  475. X */
  476. X
  477. Xchar *str_sav(s)
  478. X    char *s;
  479. X{
  480. X    char *p;
  481. X
  482. X    if ((p = malloc((unsigned)(strlen(s) + 1))) == (char *) NULL) {
  483. X        (void) fprintf(stderr, "%s: malloc: out of memory\n", Progname);
  484. X        exit(1);
  485. X    }
  486. X    (void) strcpy(p, s);
  487. X    return (p);
  488. X}
  489. X
  490. X/* D I R E C T O R Y
  491. X *
  492. X *  directory() is used to open and read the directory and pass
  493. X *  the filenames found to the routine lc();
  494. X */
  495. X#if (POSIX || BSD)
  496. Xvoid directory(dname)
  497. X    char *dname;
  498. X{
  499. X    register char *nbp;
  500. X    register char *nep;
  501. X    DIR *dstream;
  502. X    int i;
  503. X#ifdef DIRECT
  504. X    struct direct *dp;
  505. X#else
  506. X    struct dirent *dp;
  507. X#endif
  508. X
  509. X    /* add a slash to the end of the directory name */
  510. X    nbp = dname + strlen(dname);
  511. X#ifdef AMIGA
  512. X    if (*(nbp - 1) != ':') {
  513. X        *nbp++ = '/';
  514. X        *nbp = '\0';
  515. X    }
  516. X#else
  517. X    *nbp++ = '/';
  518. X    *nbp = '\0';
  519. X#endif
  520. X
  521. X    if ((dstream = opendir(dname)) == NULL) {
  522. X        (void) fprintf(stderr, "%s: can't open %s\n", Progname, dname);
  523. X        return;
  524. X    }
  525. X
  526. X    while ((dp = readdir(dstream)) != NULL) {
  527. X        if (strcmp(dp->d_name, ".") == 0
  528. X            || strcmp(dp->d_name, "..") == 0
  529. X            || (!Allfiles && *(dp->d_name) == '.'))
  530. X            continue;
  531. X
  532. X        for (i = 0, nep = nbp; dp->d_name[i] && i < NAME_MAX; i++)
  533. X            *nep++ = dp->d_name[i];
  534. X        *nep++ = '\0';
  535. X        lc(dname, 2);
  536. X    }
  537. X    (void) closedir(dstream);
  538. X    *--nbp = '\0';
  539. X    return;
  540. X}
  541. X
  542. X#else /* not POSIX or BSD */
  543. X
  544. X/* D I R E C T O R Y
  545. X *
  546. X *  directory() is used to open and read the directory and pass
  547. X *  the filenames found to the routine lc();
  548. X */
  549. Xvoid directory(dname)
  550. X    char *dname;
  551. X{
  552. X    register char *nbp;
  553. X    register char *nep;
  554. X    FILE *fd;
  555. X    int i;
  556. X    struct direct dir;
  557. X
  558. X    /* add a slash to the end of the directory name */
  559. X    nbp = dname + strlen(dname);
  560. X    *nbp++ = '/';
  561. X    *nbp = '\0';
  562. X
  563. X    if ((nbp + NAME_MAX + 2) >= (dname + BUFSIZE))  { /* dname too long */
  564. X        (void) fprintf(stderr, "%s: dirname too long: %s\n",
  565. X                       Progname, dname);
  566. X        return;
  567. X    }
  568. X
  569. X    if ((fd = fopen(dname, "r")) == (FILE *) NULL) { /* open the directory */
  570. X        (void) fprintf(stderr, "%s: can't open %s\n", Progname, dname);
  571. X        return;
  572. X    }
  573. X
  574. X    while (fread((char *) &dir, sizeof(dir), 1, fd) > 0) {
  575. X        if (dir.d_ino == 0
  576. X            || strcmp(dir.d_name, ".") == 0
  577. X            || strcmp(dir.d_name, "..") == 0)
  578. X            continue;
  579. X        for (i = 0, nep = nbp; i < NAME_MAX; i++)
  580. X            *nep++ = dir.d_name[i];
  581. X        *nep++ = '\0';
  582. X        lc(dname, 2);
  583. X    }
  584. X    (void) fclose(fd);
  585. X    *--nbp = '\0';     /* restore dname */
  586. X    return;
  587. X}
  588. X#endif
  589. X
  590. X/* G E T L I N K
  591. X *
  592. X *  getlink() calls readlink() which places the contents of the 
  593. X *  symbolic link referred to by fn in the buffer wk. The contents 
  594. X *  of the link are null terminated and the path is returned to
  595. X *  the calling funtion as a pointer to a storage area created
  596. X *  by using malloc(3);
  597. X */
  598. X
  599. X#ifdef S_IFLNK
  600. Xchar *getlink(fn)
  601. X    char *fn;
  602. X{
  603. X    char wk[PATH_MAX + 1];
  604. X    int rc;
  605. X
  606. X    rc = readlink(fn, wk, sizeof(wk));
  607. X    if (rc < 0)
  608. X        return ((char *) NULL);
  609. X    wk[rc] = '\0';
  610. X    return (str_sav(wk));
  611. X}
  612. X#endif
  613. X
  614. X/* P R I N T _ L I N E 
  615. X *
  616. X *  print_line() is used to format and output the files previously 
  617. X *  located. This routine could use a lot more smarts but currently
  618. X *  it is rather crude...
  619. X */
  620. X
  621. Xint print_line(files, ind)
  622. X    struct list *files;
  623. X    int ind;
  624. X{
  625. X    register char *frmt;
  626. X    char out_str[PATH_MAX + 3];
  627. X    int i;
  628. X    int prt_limit;
  629. X
  630. X    if (Single) {
  631. X#ifdef S_IFLNK
  632. X        if (Current == LNK_ONLY) {
  633. X            if (*(Lnksn.names + ind) != (char *) NULL)
  634. X                (void) printf("    %s -> %s\n",
  635. X                              *(Lnks.names + ind), *(Lnksn.names + ind));
  636. X            else
  637. X                (void) printf("    %s -> %s\n",
  638. X                              *(Lnks.names + ind), "UNRESOLVED");
  639. X            ind++;
  640. X            return (ind);
  641. X        }
  642. X#endif
  643. X        (void) puts(*(files->names + ind));
  644. X        ind++;
  645. X    }
  646. X    else if (Maxlen > ((Screen_width - 4) / 2)) {
  647. X        (void) printf("    %s\n", *(files->names + ind));
  648. X        ind++;
  649. X    }
  650. X    else {
  651. X         frmt = out_str;
  652. X         for (i = 0; i < 4; i++)
  653. X              *frmt++ = ' ';
  654. X
  655. X        /* The prt_limit may need to be smarter */
  656. X
  657. X         prt_limit = (Screen_width - 4) / (Maxlen + 1);
  658. X         if (Maxlen == 3 || Maxlen == 1)
  659. X             prt_limit--;
  660. X
  661. X         while ((ind < files->num) && (prt_limit-- > 0)) {
  662. X              i = 0;
  663. X              do {
  664. X                   if (*(*(files->names + ind) + i) == '\0') {
  665. X                       while (i++ <= Maxlen)
  666. X                             *frmt++ = ' ';
  667. X                   }
  668. X                   else
  669. X                       *frmt++ = *(*(files->names + ind) + i);
  670. X                   i++;
  671. X              } while (i <= Maxlen);
  672. X              ind++;
  673. X         }
  674. X         *frmt = '\0';
  675. X         (void) puts(out_str);
  676. X    }
  677. X    return (ind);
  678. X}
  679. X
  680. X/* S T R _ C M P
  681. X *
  682. X *  str_cmp is the comparison routine used by
  683. X *  qsort(3) to order the filenames inplace.
  684. X */
  685. X
  686. Xint str_cmp(s1, s2)
  687. X    char **s1;
  688. X    char **s2;
  689. X{
  690. X    return strcmp(*s1, *s2);
  691. X}
  692. X
  693. X/* P R _ I N F O
  694. X *
  695. X *  pr_info() is used to sort the data if required
  696. X *  and then pass it to print_line to actually output
  697. X *  the data.`
  698. X */
  699. X
  700. Xint pr_info(strng, files, flg, sort_needed)
  701. X    char *strng;
  702. X    struct list *files;
  703. X    int flg;
  704. X    int sort_needed;
  705. X{
  706. X    int pnum = 0;
  707. X
  708. X#ifdef LENS
  709. X    Maxlen = files->maxlen;
  710. X#endif
  711. X
  712. X#ifdef S_IFLNK
  713. X    if (!Single || Current == LNK_ONLY) {
  714. X        if (flg)
  715. X            (void) puts("");
  716. X        (void) puts(strng);
  717. X    }
  718. X#else
  719. X    if (!Single) {
  720. X        if (flg)
  721. X            (void) puts("");
  722. X        (void) puts(strng);
  723. X    }
  724. X#endif
  725. X
  726. X    if (sort_needed)
  727. X        qsort((char *) (files->names), files->num,
  728. X              sizeof(char *), str_cmp);
  729. X
  730. X    do {
  731. X        pnum = print_line(files, pnum);
  732. X    } while (pnum < files->num);
  733. X
  734. X    return (1);
  735. X}
  736. X
  737. X/* P R I N T _ I N F O
  738. X *
  739. X *  print_info() is called to display all the filenames
  740. X *  located in the directory reading and storage functions.
  741. X */
  742. X
  743. Xvoid print_info()
  744. X{
  745. X    int flag = 0;
  746. X
  747. X#ifdef S_IFLNK
  748. X    int ssing;
  749. X
  750. X    Current = 0;
  751. X
  752. X    if (Lnks.num > 0 && (Disp_links == TRUE || Only == LNK_ONLY)) {
  753. X        ssing = Single;
  754. X        Single = TRUE;
  755. X        Current = LNK_ONLY;
  756. X        flag = pr_info("Symbolic Links: ", &Lnks, flag, 0);
  757. X        Single = ssing;
  758. X    }
  759. X#endif
  760. X
  761. X#ifdef S_IFSOCK
  762. X    if (Socks.num > 0 && (Only == 0 || Only == SOCK_ONLY))
  763. X        flag = pr_info("Sockets: ", &Socks, flag, Sort_wanted);
  764. X#endif
  765. X
  766. X#ifdef S_IFNAM
  767. X    if (Sems.num > 0 && (Only == 0 || Only == SEM_ONLY))
  768. X        flag = pr_info("Semaphore Files: ", &Sems, flag, Sort_wanted);
  769. X
  770. X    if (Sds.num > 0 && (Only == 0 || Only == SD_ONLY))
  771. X        flag = pr_info("Shared Data Files: ", &Sds, flag, Sort_wanted);
  772. X#endif
  773. X
  774. X#ifdef S_IFIFO
  775. X    if (Fifos.num > 0 && (Only == 0 || Only == FIFO_ONLY))
  776. X        flag = pr_info("Fifo Files: ", &Fifos, flag, Sort_wanted);
  777. X#endif
  778. X
  779. X#ifdef S_IFCHR
  780. X    if (Chrs.num > 0 && (Only == 0 || Only == CHAR_ONLY))
  781. X        flag = pr_info("Character Special Files: ", &Chrs, flag, Sort_wanted);
  782. X#endif
  783. X
  784. X#ifdef S_IFBLK
  785. X    if (Blks.num > 0 && (Only == 0 || Only == BLOCK_ONLY))
  786. X        flag = pr_info("Block Special Files: ", &Blks, flag, Sort_wanted);
  787. X#endif
  788. X
  789. X    if (Dirs.num > 0 && (Only == 0 || Only == DIR_ONLY))
  790. X        flag = pr_info("Directories: ", &Dirs, flag, Sort_wanted);
  791. X
  792. X    if (Fls.num > 0 && (Only == 0 || Only == FILE_ONLY))
  793. X        flag = pr_info("Files: ", &Fls, flag, Sort_wanted);
  794. X
  795. X    return;
  796. X}
  797. X
  798. X/* B A S E N A M E
  799. X *
  800. X *  basename() is used to return the base file name of a
  801. X *  path refered to by name. The base file name is stored
  802. X *  into a storage location refered to by str. It is the
  803. X *  calling function's responsibility to assure adequate
  804. X *  storage is supplied.
  805. X */
  806. X
  807. Xvoid basename(name, str)
  808. X    char *name;
  809. X    char *str;
  810. X{
  811. X    char *p;
  812. X
  813. X    if ((p = strrchr(name, '/')) == (char *) NULL) {
  814. X#ifdef AMIGA
  815. X        if ((p = strchr(name, ':')) == (char *) NULL)
  816. X            (void) strcpy(str, name);
  817. X        else
  818. X            (void) strcpy(str, ++p);
  819. X#else
  820. X        (void) strcpy(str, name);
  821. X#endif
  822. X    }
  823. X    else
  824. X        (void) strcpy(str, ++p);
  825. X    return;
  826. X}
  827. X
  828. X/* A D D _ T O _ L I S T
  829. X *
  830. X * add_to_list() is used to add the supplied filename refered to
  831. X * by str to the appropriate category storage array.
  832. X */
  833. X
  834. Xvoid add_to_list(files, str)
  835. X    struct list *files;
  836. X    char *str;
  837. X{
  838. X    if (files->max == 0) {
  839. X        files->names = (char **) malloc(sizeof(char *) * NODES_PER_HUNK);
  840. X        if (files->names == (char **) NULL) {
  841. X            (void) fprintf(stderr,
  842. X                           "%s: malloc: out of memory\n", Progname);
  843. X            exit(1);
  844. X        }
  845. X        files->max = NODES_PER_HUNK;
  846. X    }
  847. X    else if (files->num == files->max) {
  848. X        files->names =
  849. X            (char **) realloc((char *) files->names,
  850. X                              (unsigned) sizeof(char *)
  851. X                              * (files->max + NODES_PER_HUNK));
  852. X        if (files->names == (char **) NULL) {
  853. X            (void) fprintf(stderr,
  854. X                           "%s: realloc: out of memory\n", Progname);
  855. X            exit(1);
  856. X        }
  857. X        files->max += NODES_PER_HUNK;
  858. X    }
  859. X    if (str == (char *) NULL)
  860. X        *(files->names + files->num++) = (char *) NULL;
  861. X    else
  862. X        *(files->names + files->num++) = str_sav(str);
  863. X    return;
  864. X}
  865. X
  866. X/* L C
  867. X *
  868. X * lc() is main function for determining the type of
  869. X * the file refered to by name.
  870. X */
  871. X
  872. Xvoid lc(name, cnt)
  873. X    char *name;
  874. X    int cnt;
  875. X{
  876. X#ifdef S_IFLNK
  877. X    char *link;
  878. X#endif
  879. X    char sav_str[BUFSIZE + 2];
  880. X    int mlen;
  881. X    struct stat sbuf;
  882. X
  883. X#ifdef S_IFLNK
  884. X    if (lstat(name, &sbuf) < 0) {
  885. X        (void) fprintf(stderr, "%s: can't stat %s\n", Progname, name);
  886. X        return;
  887. X    }
  888. X#else
  889. X    if (stat(name, &sbuf) == -1) {
  890. X        (void) fprintf(stderr, "%s: can't stat %s\n", Progname, name);
  891. X        return;
  892. X    }
  893. X#endif
  894. X
  895. X    basename(name, sav_str);
  896. X    mlen = strlen(sav_str);
  897. X
  898. X#ifndef LENS
  899. X    if (mlen > Maxlen)
  900. X        Maxlen = mlen;
  901. X#endif
  902. X
  903. X    switch (sbuf.st_mode & S_IFMT) {
  904. X
  905. X    case S_IFDIR:
  906. X        if (!Allfiles && sav_str[0] == '.' && Level != 0)
  907. X            break;
  908. X        if (cnt != 1)        /* dont store the dir name on entry */
  909. X            add_to_list(&Dirs, sav_str);
  910. X        /* never called - left for expansion to recursive     */
  911. X        /* searches of subdirectories. Right, re-write needed */
  912. X        /* in output facilities first...                      */
  913. X        if (Level++ == 0)
  914. X            directory(name);
  915. X#ifdef LENS
  916. X        if (mlen > Dirs.maxlen)
  917. X            Dirs.maxlen = mlen;
  918. X#endif
  919. X        break;
  920. X
  921. X    case S_IFREG:
  922. X        /* do not print .files unless enviromental variable */
  923. X        /* or option set.                                   */
  924. X        if (!Allfiles && sav_str[0] == '.')
  925. X            break;
  926. X        if (Executables
  927. X            && (sbuf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) {
  928. X            *(sav_str + mlen) = '*';
  929. X            ++mlen;
  930. X            *(sav_str + mlen) = '\0';
  931. X#ifndef LENS
  932. X            if (mlen > Maxlen)
  933. X                Maxlen = mlen;
  934. X#endif
  935. X        }
  936. X        add_to_list(&Fls, sav_str);
  937. X#ifdef LENS
  938. X        if (mlen > Fls.maxlen)
  939. X            Fls.maxlen = mlen;
  940. X#endif
  941. X        break;
  942. X
  943. X#ifdef S_IFCHR
  944. X    case S_IFCHR:
  945. X        if (!Allfiles && sav_str[0] == '.')
  946. X            break;
  947. X        add_to_list(&Chrs, sav_str);
  948. X#ifdef LENS
  949. X        if (mlen > Chrs.maxlen)
  950. X            Chrs.maxlen = mlen;
  951. X#endif
  952. X        break;
  953. X#endif
  954. X
  955. X#ifdef S_IFBLK
  956. X    case S_IFBLK:
  957. X        if (!Allfiles && sav_str[0] == '.')
  958. X            break;
  959. X        add_to_list(&Blks, sav_str);
  960. X#ifdef LENS
  961. X        if (mlen > Blks.maxlen)
  962. X            Blks.maxlen = mlen;
  963. X#endif
  964. X        break;
  965. X#endif
  966. X
  967. X#ifdef S_IFIFO
  968. X    case S_IFIFO:
  969. X        if (!Allfiles && sav_str[0] == '.')
  970. X            break;
  971. X        add_to_list(&Fifos, sav_str);
  972. X#ifdef LENS
  973. X        if (mlen > Fifos.maxlen)
  974. X            Fifos.maxlen = mlen;
  975. X#endif
  976. X        break;
  977. X#endif
  978. X
  979. X#ifdef S_IFLNK
  980. X    case S_IFLNK:
  981. X        if (!Allfiles && sav_str[0] == '.')
  982. X            break;
  983. X        add_to_list(&Lnks, sav_str);
  984. X        link = getlink(name);
  985. X        add_to_list(&Lnksn, link);
  986. X        if (link != (char *) NULL)
  987. X            free(link);
  988. X#ifdef LENS
  989. X        if (mlen > Lnks.maxlen)
  990. X            Lnks.maxlen = mlen;
  991. X#endif
  992. X        if (stat(name, &sbuf) < 0) {
  993. X            if (!Ignore) 
  994. X                (void) fprintf(stderr,
  995. X                               "%s: %s: can't resolve symbolic link\n",
  996. X                               Progname, name);
  997. X        }
  998. X        else {
  999. X            if (Mark_links) {
  1000. X                *(sav_str + mlen) = '@';
  1001. X                ++mlen;
  1002. X                *(sav_str + mlen) = '\0';
  1003. X#ifndef LENS
  1004. X                if (mlen > Maxlen)
  1005. X                    Maxlen = mlen;
  1006. X#endif
  1007. X            }
  1008. X
  1009. X            switch (sbuf.st_mode & S_IFMT) {
  1010. X
  1011. X            case S_IFDIR:
  1012. X                if (cnt != 1)        /*dont store the dir name on entry */
  1013. X                    add_to_list(&Dirs, sav_str);
  1014. X                /* never called - left for expansion to recursive */
  1015. X                /* searches of subdirectories                     */
  1016. X                if (Level++ == 0)
  1017. X                    directory(name);
  1018. X#ifdef LENS
  1019. X                if (mlen > Dirs.maxlen)
  1020. X                    Dirs.maxlen = mlen;
  1021. X#endif
  1022. X                break;
  1023. X
  1024. X            case S_IFREG:
  1025. X                if (Executables
  1026. X                    && (sbuf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) {
  1027. X                    *(sav_str + mlen) = '*';
  1028. X                    ++mlen;
  1029. X                    *(sav_str + mlen) = '\0';
  1030. X#ifndef LENS
  1031. X                    if (mlen > Maxlen)
  1032. X                        Maxlen = mlen;
  1033. X#endif
  1034. X                }
  1035. X                add_to_list(&Fls, sav_str);
  1036. X#ifdef LENS
  1037. X                if (mlen > Fls.maxlen)
  1038. X                    Fls.maxlen = mlen;
  1039. X#endif
  1040. X                break;
  1041. X
  1042. X#ifdef S_IFCHR
  1043. X            case S_IFCHR:
  1044. X                add_to_list(&Chrs, sav_str);
  1045. X#ifdef LENS
  1046. X                if (mlen > Chrs.maxlen)
  1047. X                    Chrs.maxlen = mlen;
  1048. X#endif
  1049. X                break;
  1050. X#endif
  1051. X
  1052. X#ifdef S_IFBLK
  1053. X            case S_IFBLK:
  1054. X                add_to_list(&Blks, sav_str);
  1055. X#ifdef LENS
  1056. X                if (mlen > Blks.maxlen)
  1057. X                    Blks.maxlen = mlen;
  1058. X#endif
  1059. X                break;
  1060. X#endif
  1061. X
  1062. X#ifdef S_IFIFO
  1063. X            case S_IFIFO:
  1064. X                add_to_list(&Fifos, sav_str);
  1065. X#ifdef LENS
  1066. X                if (mlen > Fifos.maxlen)
  1067. X                    Fifos.maxlen = mlen;
  1068. X#endif
  1069. X                break;
  1070. X#endif
  1071. X
  1072. X#ifdef S_IFSOCK
  1073. X            case S_IFSOCK :
  1074. X                add_to_list(&Socks, sav_str);
  1075. X#ifdef LENS
  1076. X                if (mlen > Socks.maxlen)
  1077. X                    Socks.maxlen = mlen;
  1078. X#endif
  1079. X                break;
  1080. X#endif
  1081. X            }
  1082. X        }
  1083. X        break;
  1084. X#endif
  1085. X
  1086. X#ifdef S_IFSOCK
  1087. X    case S_IFSOCK:
  1088. X        if (!Allfiles && sav_str[0] == '.')
  1089. X            break;
  1090. X        add_to_list(&Socks, sav_str);
  1091. X#ifdef LENS
  1092. X        if (mlen > Socks.maxlen)
  1093. X            Socks.maxlen = mlen;
  1094. X#endif
  1095. X        break;
  1096. X#endif
  1097. X
  1098. X#ifdef S_IFNAM
  1099. X    case S_IFNAM:
  1100. X        switch (sbuf.st_rdev) {
  1101. X
  1102. X        case S_INSEM:
  1103. X            if (!Allfiles && sav_str[0] == '.')
  1104. X                break;
  1105. X            add_to_list(&Sems, sav_str);
  1106. X#ifdef LENS
  1107. X            if (mlen > Sems.maxlen)
  1108. X                Sems.maxlen = mlen;
  1109. X#endif
  1110. X            break;
  1111. X
  1112. X        case S_INSHD:
  1113. X            if (!Allfiles && sav_str[0] == '.')
  1114. X                break;
  1115. X            add_to_list(&Sds, sav_str);
  1116. X#ifdef LENS
  1117. X            if (mlen > Sds.maxlen)
  1118. X                Sds.maxlen = mlen;
  1119. X#endif
  1120. X            break;
  1121. X        }
  1122. X        break;
  1123. X#endif
  1124. X
  1125. X    }
  1126. X    return;
  1127. X}
  1128. X
  1129. X/* V A L I D _ O P T
  1130. X *
  1131. X * valid_opt() is used to translate user has supplied option
  1132. X * letters into something that this process can use. It sets
  1133. X * up the options and if a usage is requested, it sets up and
  1134. X * prints a usage message for the user.
  1135. X */
  1136. X
  1137. Xvoid valid_opt(c, usage)
  1138. X    char c;
  1139. X    int usage;
  1140. X{
  1141. X    char up[7];
  1142. X
  1143. X    up[0] = '\0';
  1144. X
  1145. X    switch(c) {
  1146. X
  1147. X    case 'a':
  1148. X        Allfiles = TRUE;
  1149. X        break;
  1150. X
  1151. X    case 'b':
  1152. X        Only = BLOCK_ONLY;
  1153. X        break;
  1154. X
  1155. X    case 'c':
  1156. X        Only = CHAR_ONLY;
  1157. X        break;
  1158. X
  1159. X    case 'd':
  1160. X        Only = DIR_ONLY;
  1161. X        break;
  1162. X
  1163. X    case 'D':
  1164. X        Display_single = FALSE;
  1165. X        break;
  1166. X
  1167. X    case 'e':
  1168. X        Executables = TRUE;
  1169. X        break;
  1170. X
  1171. X    case 'f':
  1172. X        Only = FILE_ONLY;
  1173. X        break;
  1174. X
  1175. X    case 'r':
  1176. X        Sort_wanted = FALSE;
  1177. X        break;
  1178. X
  1179. X#ifdef S_IFIFO
  1180. X    case 'F':
  1181. X        Only = FIFO_ONLY;
  1182. X        break;
  1183. X#endif
  1184. X
  1185. X    case '1':
  1186. X        Single = TRUE;
  1187. X        break;
  1188. X
  1189. X#ifdef S_IFLNK
  1190. X    case 's':
  1191. X        Only = LNK_ONLY;
  1192. X        break;
  1193. X
  1194. X    case 'l':
  1195. X        Mark_links = TRUE;
  1196. X        break;
  1197. X
  1198. X    case 'I':
  1199. X        Ignore = TRUE;
  1200. X        break;
  1201. X
  1202. X    case 'L':
  1203. X        Disp_links = TRUE;
  1204. X        break;
  1205. X#endif
  1206. X
  1207. X#ifdef S_IFSOCK
  1208. X    case 'S':
  1209. X        Only = SOCK_ONLY;
  1210. X        break;
  1211. X#endif
  1212. X
  1213. X#ifdef S_IFNAM
  1214. X    case 'm':
  1215. X        Only = SD_ONLY;
  1216. X        break;
  1217. X
  1218. X    case 'M':
  1219. X        Only = SEM_ONLY;
  1220. X        break;
  1221. X#endif
  1222. X
  1223. X    default:
  1224. X        if (usage == TRUE) {
  1225. X#ifdef S_IFLNK
  1226. X            (void) strcat(up, "IlLs");
  1227. X#endif
  1228. X#ifdef S_IFSOCK
  1229. X            (void) strcat(up, "S");
  1230. X#endif
  1231. X#ifdef S_IFNAM
  1232. X            (void) strcat(up, "mM");
  1233. X#endif
  1234. X            (void) fprintf(stderr,
  1235. X                           "usage: %s [-abcdDefF1%s] [directories or files]\n",
  1236. X                           Progname, up);
  1237. X            exit(1);
  1238. X        }
  1239. X    }
  1240. X
  1241. X    return;
  1242. X}
  1243. X
  1244. X/* S E T _ E N V _ V A R S
  1245. X *
  1246. X * set_env_vars() is used get the environment variables that
  1247. X * lc uses. The environment variable LC can be used to setup
  1248. X * the default way in which the user likes to see a directory
  1249. X * listing. Command line options override those specified in
  1250. X * the environment.
  1251. X */
  1252. X
  1253. Xvoid set_env_vars()
  1254. X{
  1255. X    char *ep;
  1256. X
  1257. X    if ((ep = getenv("COLS")) != (char *) NULL) {
  1258. X        if (sscanf(ep, "%d", &Screen_width) == 0
  1259. X            || (Screen_width != 80 && Screen_width != 132))
  1260. X            Screen_width = 80;
  1261. X    }
  1262. X
  1263. X    if ((ep = getenv("LC")) != (char *) NULL) {
  1264. X        while (*ep != '\0') {
  1265. X            valid_opt(*ep, FALSE);
  1266. X            ep++;
  1267. X        }
  1268. X    }
  1269. X
  1270. X    return;
  1271. X}
  1272. X
  1273. X/* S P D I S T:   return the distance between two names
  1274. X *
  1275. X * very rough spelling metric:
  1276. X *          0 if the strings are identical
  1277. X *          1 if two chars are transposed
  1278. X *          2 if 1 char wrong, added or deleted
  1279. X *          3 otherwise
  1280. X */
  1281. X#define EQ(s, t) (strcmp(s, t) == 0)
  1282. X
  1283. Xint spdist(s, t)
  1284. X    char *s;
  1285. X    char *t;
  1286. X{
  1287. X    while (*s++ == *t) {
  1288. X        if (*t++ == '\0')
  1289. X            return 0;             /* exact match */
  1290. X    }
  1291. X    if (*--s) {
  1292. X        if (*t) {
  1293. X            if (s[1] && t[1] && *s == t[1] && *t == s[1] && EQ(s+2, t+2))
  1294. X                return 1;             /* transposition */
  1295. X            if (EQ(s+1, t+1))
  1296. X                return 2;             /* 1 char mismatch */
  1297. X        }
  1298. X        if (EQ(s+1, t))
  1299. X            return 2;                /* extra chacter */
  1300. X    }
  1301. X    if (*t && EQ(s, t+1))
  1302. X        return 2;                   /* missing character */
  1303. X    return 3;
  1304. X}
  1305. X
  1306. X/*    M I N D I S T       
  1307. X * 
  1308. X *  mindist() searches the directory for the best guess
  1309. X *  in the event the requested file was not located.
  1310. X */
  1311. X
  1312. X#if (POSIX || BSD)
  1313. Xint mindist(dir, guess, best)    /* set best, return distance 0..3 */
  1314. X    char *dir;
  1315. X    char *guess;
  1316. X    char *best;
  1317. X{
  1318. X    DIR *dfd;
  1319. X    int d;
  1320. X    int nd;
  1321. X#ifdef DIRECT
  1322. X    struct direct *dp;
  1323. X#else
  1324. X    struct dirent *dp;
  1325. X#endif
  1326. X
  1327. X    if (dir[0] == '\0')
  1328. X        dir = ".";
  1329. X    d = 3;                      /* minimum distance */
  1330. X
  1331. X    if ((dfd = opendir(dir)) == NULL)
  1332. X        return d;
  1333. X
  1334. X    while ((dp = readdir(dfd)) != NULL) {
  1335. X        if (dp->d_ino) {
  1336. X            nd = spdist(dp->d_name, guess);
  1337. X            if (nd <= d && nd != 3) {
  1338. X                (void) strcpy(best, dp->d_name);
  1339. X                d = nd;
  1340. X                if (d == 0)    /* exact match */
  1341. X                    break;
  1342. X            }
  1343. X        }
  1344. X    }
  1345. X    (void) closedir(dfd);
  1346. X    return d;
  1347. X}
  1348. X
  1349. X#else /* not POSIX or BSD */
  1350. X
  1351. X/*    M I N D I S T       
  1352. X * 
  1353. X *  mindist() searches the directory for the best guess
  1354. X *  in the event the requested file was not located.
  1355. X */
  1356. X
  1357. Xint mindist(dir, guess, best)    /* set best, return distance 0..3 */
  1358. X    char *dir;
  1359. X    char *guess;
  1360. X    char *best;
  1361. X{
  1362. X    FILE *fd;
  1363. X    int d;
  1364. X    int nd;
  1365. X    struct {
  1366. X        ino_t ino;
  1367. X        char name[NAME_MAX + 1];   /* 1 more than in dir.h */
  1368. X    } nbuf;
  1369. X
  1370. X    nbuf.name[NAME_MAX] = '\0';   /* +1 for terminal '\0' */
  1371. X    if (dir[0] == '\0')
  1372. X        dir = ".";
  1373. X    d = 3;                      /* minimum distance */
  1374. X    if ((fd = fopen(dir, "r")) == (FILE *) NULL)
  1375. X        return d;
  1376. X    while (fread((char *) &nbuf, sizeof(struct direct), 1, fd) > 0) {
  1377. X        if (nbuf.ino) {
  1378. X            nd = spdist(nbuf.name, guess);
  1379. X            if (nd <= d && nd != 3) {
  1380. X                (void) strcpy(best, nbuf.name);
  1381. X                d = nd;
  1382. X                if (d == 0)    /* exact match */
  1383. X                    break;
  1384. X            }
  1385. X        }
  1386. X    }
  1387. X    (void) fclose(fd);
  1388. X    return d;
  1389. X}
  1390. X#endif
  1391. X
  1392. X/* S P N A M E:    return correctly spelled filename
  1393. X *
  1394. X * spname(oldname, newname) char *oldname, *newname;
  1395. X *      returns  -1 if no reasonable match to oldname,
  1396. X *                0 if exact match,
  1397. X *                1 if corrected.
  1398. X * stores corrected name in newname.
  1399. X */
  1400. X
  1401. Xint spname(oldname, newname)
  1402. X    char *oldname;
  1403. X    char *newname;
  1404. X{
  1405. X    char *new = newname;
  1406. X    char *old = oldname;
  1407. X    char *p;
  1408. X    char best[NAME_MAX + 1];
  1409. X    char guess[NAME_MAX + 1];
  1410. X
  1411. X    for (;;) {
  1412. X        while (*old == '/')   /* skip slashes */
  1413. X            *new++ = *old++;
  1414. X        *new = '\0';
  1415. X        if (*old == '\0')     /* exact or corrected */
  1416. X            return (strcmp(oldname, newname) != 0);
  1417. X        p = guess;            /* copy next component into guess */
  1418. X        for (/* void */ ; *old != '/' && *old != '\0'; old++) {
  1419. X            if (p < (guess + NAME_MAX))
  1420. X                *p++ = *old;
  1421. X        }
  1422. X        *p = '\0';
  1423. X        if (mindist(newname, guess, best) >= 3)
  1424. X            return (-1);        /* hopeless */
  1425. X        for (p = best; *new = *p++; new++)   /* add to end */
  1426. X            /* void */;
  1427. X    }
  1428. X}
  1429. X
  1430. X/*  I N _ C D P A T H
  1431. X * 
  1432. X *  in_cdpath() searches the CDPATH stored in the environment
  1433. X *  for the filename specified. If it is found, fill the
  1434. X *  storage area refered to by buffer with the corrected path.
  1435. X *  Return TRUE if located and FALSE if not located in the CDPATH.
  1436. X */
  1437. X
  1438. Xint in_cdpath(requested_dir, buffer)
  1439. X    char *requested_dir;
  1440. X    char *buffer;
  1441. X{
  1442. X    static char *cdpath;
  1443. X    static int first = 1;
  1444. X
  1445. X    char *cp;
  1446. X    char *path;
  1447. X    char patbuf[BUFSIZE + 1];
  1448. X    int quit;
  1449. X
  1450. X    if (first) {
  1451. X        if ((cdpath = getenv("CDPATH")) != (char *) NULL)
  1452. X            cdpath = str_sav(cdpath);
  1453. X        first = 0;
  1454. X    }
  1455. X
  1456. X    if (cdpath == (char *) NULL)
  1457. X        return (0);
  1458. X
  1459. X    (void) strcpy(patbuf, cdpath);
  1460. X    path = patbuf;
  1461. X
  1462. X    quit = 0;
  1463. X
  1464. X    while (!quit) {
  1465. X        cp = strchr(path, ':');
  1466. X        if (cp == (char *) NULL)
  1467. X            quit++;
  1468. X        else
  1469. X            *cp = '\0';
  1470. X
  1471. X        if (*(path + 1) == '\0' && *path == '/')
  1472. X            (void) sprintf(buffer, "/%s", requested_dir);
  1473. X        else
  1474. X            (void) sprintf(buffer, "%s/%s",
  1475. X                           (*path ? path : "."), requested_dir);
  1476. X
  1477. X        if (access(buffer, 1) == 0)
  1478. X            return (TRUE);
  1479. X        path = ++cp;
  1480. X    }
  1481. X    return (FALSE);
  1482. X}
  1483. X
  1484. X/*  M A I N 
  1485. X * 
  1486. X *  Ye olde main();
  1487. X */
  1488. X
  1489. Xint main(argc, argv)
  1490. X    int argc;
  1491. X    char *argv[];
  1492. X{
  1493. X    char *argp;
  1494. X#ifdef S_IFLNK
  1495. X    char *link;
  1496. X#endif
  1497. X    char buf[BUFSIZE + 1];
  1498. X    int idx;
  1499. X    int nl;
  1500. X    struct stat sbuf;
  1501. X
  1502. X    nl = idx = FALSE;
  1503. X
  1504. X    Progname = argv[0];
  1505. X
  1506. X    set_env_vars();                       /* get environment variables */
  1507. X
  1508. X    /* All command line arguments must be */
  1509. X    /* lumped together such as `lc -aef`  */
  1510. X
  1511. X    if (argc > 1 && argv[1][0] == '-') {  /* if first parm is command */
  1512. X        argp = argv[1];
  1513. X
  1514. X        while (*(++argp))
  1515. X            valid_opt(*argp, TRUE);
  1516. X
  1517. X        ++argv;
  1518. X        --argc;
  1519. X    }
  1520. X
  1521. X    /*
  1522. X    ** The user has not specified a file or directory 
  1523. X    ** to be examined so assume that the current directory
  1524. X    ** is what the user is requesting.
  1525. X    */
  1526. X    if (argc == 1) {
  1527. X        (void) strcpy(buf, ".");
  1528. X        lc(buf, 1);
  1529. X        print_info();
  1530. X        return(0);
  1531. X    }
  1532. X
  1533. X    /*
  1534. X    ** The user has specified at least one file or 
  1535. X    ** directory to be examined.
  1536. X    */
  1537. X    if (argc > 2)
  1538. X        nl = TRUE;
  1539. X    while (--argc > 0) {
  1540. X        ++argv;
  1541. X        (void) strcpy(buf, *argv);
  1542. Xskipit:
  1543. X        if (stat(buf, &sbuf) == -1) {
  1544. X            if (in_cdpath(*argv, buf) || (spname(*argv, buf) != -1)) {
  1545. X                /*
  1546. X                 ** Check to see if the requested is in the CDPATH
  1547. X                 ** and if not try to correct for typos. Always print
  1548. X                 ** the name of what was found...
  1549. X                 */
  1550. X                nl = TRUE;
  1551. X                goto skipit;
  1552. X            }
  1553. X            else
  1554. X                (void)fprintf(stderr, "%s: can't find %s\n",
  1555. X                              Progname, *argv);
  1556. X        }
  1557. X        else {
  1558. X            switch (sbuf.st_mode & S_IFMT) {
  1559. X
  1560. X            case S_IFREG:
  1561. X                if (Display_single)
  1562. X                   (void) printf("%s: file\n", buf);
  1563. X                break;
  1564. X
  1565. X#ifdef S_IFCHR
  1566. X            case S_IFCHR:
  1567. X                if (Display_single)
  1568. X                   (void) printf("%s: character special file\n", buf);
  1569. X                break;
  1570. X#endif
  1571. X
  1572. X#ifdef S_IFBLK
  1573. X            case S_IFBLK:
  1574. X                if (Display_single)
  1575. X                   (void) printf("%s: block special file\n", buf);
  1576. X                break;
  1577. X#endif
  1578. X
  1579. X#ifdef S_IFIFO
  1580. X            case S_IFIFO:
  1581. X                if (Display_single)
  1582. X                   (void) printf("%s: fifo file\n", buf);
  1583. X                break;
  1584. X#endif
  1585. X
  1586. X#ifdef S_IFSOCK
  1587. X            case S_IFSOCK:
  1588. X                if (Display_single)
  1589. X                   (void) printf("%s: socket file\n", buf);
  1590. X                break;
  1591. X#endif
  1592. X
  1593. X#ifdef S_IFLNK
  1594. X            case S_IFLNK:
  1595. X                if (Display_single) {
  1596. X                   if ((link = getlink(buf)) != (char *) NULL) {
  1597. X                       (void) printf("%s: symbolic link to %s\n",
  1598. X                                     buf, link);
  1599. X                       free(link);
  1600. X                   }
  1601. X                   else
  1602. X                       (void) printf("%s: unresolved symbolic link\n",
  1603. X                                     buf);
  1604. X                }
  1605. X                break;
  1606. X#endif
  1607. X
  1608. X#ifdef S_IFNAM
  1609. X            case S_IFNAM:
  1610. X                if (Display_single) {
  1611. X                   if (sbuf.st_rdev == S_INSHD)
  1612. X                       (void) printf("%s: shared memory file\n", buf);
  1613. X                   if (sbuf.st_rdev == S_INSEM)
  1614. X                       (void) printf("%s: semaphore file\n", buf);
  1615. X                }
  1616. X                break;
  1617. X#endif
  1618. X
  1619. X            case S_IFDIR:
  1620. X                Maxlen = Level = 0;
  1621. X#ifdef S_IFBLK
  1622. X                Blks.num = 0;
  1623. X#ifdef LENS
  1624. X                Blks.maxlen = 0;
  1625. X#endif
  1626. X#endif
  1627. X
  1628. X#ifdef S_IFCHR
  1629. X                Chrs.num = 0;
  1630. X#ifdef LENS
  1631. X                Chrs.maxlen = 0;
  1632. X#endif
  1633. X#endif
  1634. X
  1635. X                Dirs.num = Fls.num = 0;
  1636. X#ifdef LENS
  1637. X                Dirs.maxlen = Fls.maxlen = 0;
  1638. X#endif
  1639. X
  1640. X#ifdef S_IFIFO
  1641. X                Fifos.num = 0;
  1642. X#ifdef LENS
  1643. X                Fifos.maxlen = 0;
  1644. X#endif
  1645. X#endif
  1646. X
  1647. X#ifdef S_IFLNK
  1648. X                Lnks.num = Lnksn.num = 0;
  1649. X#ifdef LENS
  1650. X                Lnks.maxlen = Lnksn.maxlen = 0;
  1651. X#endif
  1652. X#endif
  1653. X
  1654. X#ifdef S_IFSOCK
  1655. X                Socks.num = 0;
  1656. X#ifdef LENS
  1657. X                Socks.maxlen = 0;
  1658. X#endif
  1659. X#endif
  1660. X
  1661. X#ifdef S_IFNAM
  1662. X                Sds.num = Sems.num = 0;
  1663. X#ifdef LENS
  1664. X                Sds.maxlen = Sems.maxlen = 0;
  1665. X#endif
  1666. X#endif
  1667. X
  1668. X                if (nl == TRUE) {
  1669. X                    if (idx > 0)
  1670. X                        (void) puts("");
  1671. X                    else
  1672. X                        ++idx;
  1673. X                    (void) fputs(": ", stdout);
  1674. X                    (void) fputs(buf, stdout);
  1675. X                    (void) puts(" :");
  1676. X                }
  1677. X                lc(buf, 1);
  1678. X                print_info();
  1679. X                break;
  1680. X
  1681. X            default:
  1682. X                (void) printf("%s: unknown file type\n", buf);
  1683. X                break;
  1684. X            }
  1685. X        }
  1686. X    }
  1687. X    return(0);
  1688. X}
  1689. END_OF_FILE
  1690. if test 35879 -ne `wc -c <'lc.c'`; then
  1691.     echo shar: \"'lc.c'\" unpacked with wrong size!
  1692. fi
  1693. # end of 'lc.c'
  1694. fi
  1695. if test -f 'lc.mk' -a "${1}" != "-c" ; then 
  1696.   echo shar: Will not clobber existing file \"'lc.mk'\"
  1697. else
  1698. echo shar: Extracting \"'lc.mk'\" \(3469 characters\)
  1699. sed "s/^X//" >'lc.mk' <<'END_OF_FILE'
  1700. X#
  1701. X#    "@(#)lc.mk    1.6 9/7/90 - Kent Landfield (c) 1984,1985,1986,1987,1988,1989,1990
  1702. X#
  1703. X# This makefile is used to compile lc. 
  1704. X#
  1705. X#      Initially designed on an IBM-XT running Coherent in 1984.
  1706. X#      Ported to XENIX on an IBM-AT in 1984.
  1707. X#      Ported to System V on AT&T 3Bs in 1985.
  1708. X#      Ported to DEC Vax 11/750 running System V in 1986.
  1709. X#      Ported to BSD4.2 on a Sequent Balance 8000 in 1986.
  1710. X#      Jeff Minnig added the initial support for links. 
  1711. X#      Ported to SunOS 4.0 on a Sun 3/60 in 1988.
  1712. X#      Rick Ohnemus did major surgery to remove static storage
  1713. X#      and *greatly* enhanced the link support. Thanks rick!
  1714. X#      Tested with Ultrix 3.0 & 3.1 on a DECstation 3100 in 1989.
  1715. X#      Tested with Ultrix 3.0 & 3.1 on a VAXstation 3500 in 1989.
  1716. X#      Ported to AIX 2.2 on an IBM RT.
  1717. X#      Tested with UTek on a Tektronix 4319 in 1989.
  1718. X#      Tested with IRIX System V on a Silicon Graphics Iris 4D/210GTX in 1989.
  1719. X#      Tested with AmigaDOS 1.3 on an Amiga 1000 in 1989.
  1720. X#      Tested with SunOS 4.0.3 on a Sparkstation 1 in 1989.
  1721. X#      Tested with UTek on a Tektronix XD8810 in 1989.
  1722. X#      Runs on AIX 3.+ on a Risc System/6000 in 1990. 
  1723. X#                                                               
  1724. XI =    /usr/include
  1725. XS =     $(I)/sys
  1726. X#
  1727. X#  Have a favorite C compiler that is not cc... Too bad. ;-)
  1728. XCC=cc
  1729. X#CC=gcc
  1730. X#
  1731. X# If you are running on a BSD 4.2 box:
  1732. X# (note - if compiling on a sequent in att environment...`ucb make -f lc.mk`)
  1733. X# FLAGS = -DBSD -DDIRECT
  1734. X#         or
  1735. X#
  1736. X# If you are running on a BSD (4.3 or later), SunOS (4.0 or later),
  1737. X# or Ultrix (3.0 or later) box:
  1738. XFLAGS = -DBSD
  1739. X#         or
  1740. X#
  1741. X# If you are running on an Ultrix box and using the POSIX environment:
  1742. X# FLAGS = -DPOSIX
  1743. X#         or
  1744. X#
  1745. X# If you are running on a Xenix box:
  1746. X# FLAGS = -DXENIX
  1747. X#         or
  1748. X#
  1749. X# If you are running System V or AIX 2.2:
  1750. X# FLAGS = 
  1751. X#         or
  1752. X#
  1753. X# This runs on AIX but it does not lint well due to the include
  1754. X# files on AIX. It works, that's all I can say...
  1755. X# If you are running AIX 3.0 or later:
  1756. X# FLAGS = -D_BSD -DBSD
  1757. X#         or
  1758. X#
  1759. X# If you are running System V with Doug Gwyn's directory routines
  1760. X# or Silicon Graphics or Utek 3.2d:
  1761. X# FLAGS = -DPOSIX
  1762. X# 
  1763. X#
  1764. X# OPTIM is used for setting debugging or optimizing
  1765. X# flags for the compilation.
  1766. X#OPTIM=-O -Wall
  1767. XOPTIM=-O
  1768. X
  1769. X# Are the directory routines in another library ?
  1770. X# Or do you wish to use shared libraries ?
  1771. X# Add additional libraries here...
  1772. X# LDFLAGS = -lndir
  1773. X# LDFLAGS = -lc_s
  1774. XLDFLAGS = 
  1775. X#
  1776. X# 'qsort' function in C library
  1777. X# QSORTO =
  1778. X# QSORTC =
  1779. X#
  1780. X# 'qsort' function not in C library Or Your qsort library
  1781. X# function is slooow.
  1782. X#
  1783. X# QSORTO = qsort.o
  1784. X# QSORTC = qsort.c
  1785. X#
  1786. X# Installation ownership and directory. Customize
  1787. X# for your installation. Warning, if you do not
  1788. X# install this on your root partition, it will
  1789. X# not be available for use in single user mode.
  1790. X# Yes I know that this last statement is obvious
  1791. X# but it is extremely irritating not to have it
  1792. X# available...
  1793. X#
  1794. X# This is one program that we use so often, that we have set
  1795. X# the sticky bit on (chmod +t lc) on our older systems were
  1796. X# it matters...
  1797. X#
  1798. XBINDIR=/bin
  1799. XMODE=755
  1800. XOWNER=bin
  1801. XGROUP=bin
  1802. X
  1803. XCFLAGS = $(OPTIM) $(FLAGS)
  1804. XOBJS = lc.o $(QSORTO)
  1805. X
  1806. Xlc: lc.o $(QSORTO)
  1807. X    $(CC) $(CFLAGS) lc.o $(QSORTO) -o lc $(LDFLAGS)
  1808. X
  1809. Xlint:
  1810. X    lint $(FLAGS) $(QSORTC) lc.c
  1811. X
  1812. Xclean:
  1813. X    rm -f $(OBJS)
  1814. X
  1815. Xclobber: clean
  1816. X    rm -f lc
  1817. X
  1818. Xinstall: lc
  1819. X    strip lc
  1820. X    cp lc $(BINDIR)/lc
  1821. X    chmod $(MODE)  $(BINDIR)/lc
  1822. X    chown $(OWNER) $(BINDIR)/lc
  1823. X    chgrp $(GROUP) $(BINDIR)/lc
  1824. END_OF_FILE
  1825. if test 3469 -ne `wc -c <'lc.mk'`; then
  1826.     echo shar: \"'lc.mk'\" unpacked with wrong size!
  1827. fi
  1828. # end of 'lc.mk'
  1829. fi
  1830. if test -f 'qsort.c' -a "${1}" != "-c" ; then 
  1831.   echo shar: Will not clobber existing file \"'qsort.c'\"
  1832. else
  1833. echo shar: Extracting \"'qsort.c'\" \(6230 characters\)
  1834. sed "s/^X//" >'qsort.c' <<'END_OF_FILE'
  1835. X/*
  1836. X * Copyright (c) 1980 Regents of the University of California.
  1837. X * All rights reserved.
  1838. X *
  1839. X * Redistribution and use in source and binary forms are permitted
  1840. X * provided that the above copyright notice and this paragraph are
  1841. X * duplicated in all such forms and that any documentation,
  1842. X * advertising materials, and other materials related to such
  1843. X * distribution and use acknowledge that the software was developed
  1844. X * by the University of California, Berkeley.  The name of the
  1845. X * University may not be used to endorse or promote products derived
  1846. X * from this software without specific prior written permission.
  1847. X * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
  1848. X * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  1849. X * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  1850. X */
  1851. X
  1852. X#if !defined(lint)
  1853. Xstatic char sccsid[] = "@(#)qsort.c    5.4 (Berkeley) 6/27/88";
  1854. X#endif /* LIBC_SCCS and not lint */
  1855. X
  1856. X/*
  1857. X * qsort.c:
  1858. X * Our own version of the system qsort routine which is faster by an average
  1859. X * of 25%, with lows and highs of 10% and 50%.
  1860. X * The THRESHold below is the insertion sort threshold, and has been adjusted
  1861. X * for records of size 48 bytes.
  1862. X * The MTHREShold is where we stop finding a better median.
  1863. X */
  1864. X
  1865. X#define        THRESH        4        /* threshold for insertion */
  1866. X#define        MTHRESH        6        /* threshold for median */
  1867. X
  1868. Xstatic  int        (*qcmp)();        /* the comparison routine */
  1869. Xstatic  int        qsz;            /* size of each record */
  1870. Xstatic  int        thresh;            /* THRESHold in chars */
  1871. Xstatic  int        mthresh;        /* MTHRESHold in chars */
  1872. X
  1873. X/*
  1874. X * qst:
  1875. X * Do a quicksort
  1876. X * First, find the median element, and put that one in the first place as the
  1877. X * discriminator.  (This "median" is just the median of the first, last and
  1878. X * middle elements).  (Using this median instead of the first element is a big
  1879. X * win).  Then, the usual partitioning/swapping, followed by moving the
  1880. X * discriminator into the right place.  Then, figure out the sizes of the two
  1881. X * partions, do the smaller one recursively and the larger one via a repeat of
  1882. X * this code.  Stopping when there are less than THRESH elements in a partition
  1883. X * and cleaning up with an insertion sort (in our caller) is a huge win.
  1884. X * All data swaps are done in-line, which is space-losing but time-saving.
  1885. X * (And there are only three places where this is done).
  1886. X */
  1887. X
  1888. Xstatic void
  1889. Xqst(base, max)
  1890. X    char *base, *max;
  1891. X{
  1892. X    register char c, *i, *j, *jj;
  1893. X    register int ii;
  1894. X    char *mid, *tmp;
  1895. X    int lo, hi;
  1896. X
  1897. X    /*
  1898. X     * At the top here, lo is the number of characters of elements in the
  1899. X     * current partition.  (Which should be max - base).
  1900. X     * Find the median of the first, last, and middle element and make
  1901. X     * that the middle element.  Set j to largest of first and middle.
  1902. X     * If max is larger than that guy, then it's that guy, else compare
  1903. X     * max with loser of first and take larger.  Things are set up to
  1904. X     * prefer the middle, then the first in case of ties.
  1905. X     */
  1906. X    lo = max - base;        /* number of elements as chars */
  1907. X    do    {
  1908. X        mid = i = base + qsz * ((lo / qsz) >> 1);
  1909. X        if (lo >= mthresh) {
  1910. X            j = (qcmp((jj = base), i) > 0 ? jj : i);
  1911. X            if (qcmp(j, (tmp = max - qsz)) > 0) {
  1912. X                /* switch to first loser */
  1913. X                j = (j == jj ? i : jj);
  1914. X                if (qcmp(j, tmp) < 0)
  1915. X                    j = tmp;
  1916. X            }
  1917. X            if (j != i) {
  1918. X                ii = qsz;
  1919. X                do    {
  1920. X                    c = *i;
  1921. X                    *i++ = *j;
  1922. X                    *j++ = c;
  1923. X                } while (--ii);
  1924. X            }
  1925. X        }
  1926. X        /*
  1927. X         * Semi-standard quicksort partitioning/swapping
  1928. X         */
  1929. X        for (i = base, j = max - qsz; ; ) {
  1930. X            while (i < mid && qcmp(i, mid) <= 0)
  1931. X                i += qsz;
  1932. X            while (j > mid) {
  1933. X                if (qcmp(mid, j) <= 0) {
  1934. X                    j -= qsz;
  1935. X                    continue;
  1936. X                }
  1937. X                tmp = i + qsz;    /* value of i after swap */
  1938. X                if (i == mid) {
  1939. X                    /* j <-> mid, new mid is j */
  1940. X                    mid = jj = j;
  1941. X                } else {
  1942. X                    /* i <-> j */
  1943. X                    jj = j;
  1944. X                    j -= qsz;
  1945. X                }
  1946. X                goto swap;
  1947. X            }
  1948. X            if (i == mid) {
  1949. X                break;
  1950. X            } else {
  1951. X                /* i <-> mid, new mid is i */
  1952. X                jj = mid;
  1953. X                tmp = mid = i;    /* value of i after swap */
  1954. X                j -= qsz;
  1955. X            }
  1956. X        swap:
  1957. X            ii = qsz;
  1958. X            do    {
  1959. X                c = *i;
  1960. X                *i++ = *jj;
  1961. X                *jj++ = c;
  1962. X            } while (--ii);
  1963. X            i = tmp;
  1964. X        }
  1965. X        /*
  1966. X         * Look at sizes of the two partitions, do the smaller
  1967. X         * one first by recursion, then do the larger one by
  1968. X         * making sure lo is its size, base and max are update
  1969. X         * correctly, and branching back.  But only repeat
  1970. X         * (recursively or by branching) if the partition is
  1971. X         * of at least size THRESH.
  1972. X         */
  1973. X        i = (j = mid) + qsz;
  1974. X        if ((lo = j - base) <= (hi = max - i)) {
  1975. X            if (lo >= thresh)
  1976. X                qst(base, j);
  1977. X            base = i;
  1978. X            lo = hi;
  1979. X        } else {
  1980. X            if (hi >= thresh)
  1981. X                qst(i, max);
  1982. X            max = j;
  1983. X        }
  1984. X    } while (lo >= thresh);
  1985. X    return;
  1986. X}
  1987. X
  1988. X/*
  1989. X * qsort:
  1990. X * First, set up some global parameters for qst to share.  Then, quicksort
  1991. X * with qst(), and then a cleanup insertion sort ourselves.  Sound simple?
  1992. X * It's not...
  1993. X */
  1994. X
  1995. Xvoid
  1996. Xqsort(base, n, size, compar)
  1997. X    char    *base;
  1998. X    int    n;
  1999. X    int    size;
  2000. X    int    (*compar)();
  2001. X{
  2002. X    register char c, *i, *j, *lo, *hi;
  2003. X    char *min, *max;
  2004. X
  2005. X    if (n <= 1)
  2006. X        return;
  2007. X    qsz = size;
  2008. X    qcmp = compar;
  2009. X    thresh = qsz * THRESH;
  2010. X    mthresh = qsz * MTHRESH;
  2011. X    max = base + n * qsz;
  2012. X    if (n >= THRESH) {
  2013. X        qst(base, max);
  2014. X        hi = base + thresh;
  2015. X    } else {
  2016. X        hi = max;
  2017. X    }
  2018. X    /*
  2019. X     * First put smallest element, which must be in the first THRESH, in
  2020. X     * the first position as a sentinel.  This is done just by searching
  2021. X     * the first THRESH elements (or the first n if n < THRESH), finding
  2022. X     * the min, and swapping it into the first position.
  2023. X     */
  2024. X    for (j = lo = base; (lo += qsz) < hi; )
  2025. X        if (qcmp(j, lo) > 0)
  2026. X            j = lo;
  2027. X    if (j != base) {
  2028. X        /* swap j into place */
  2029. X        for (i = base, hi = base + qsz; i < hi; ) {
  2030. X            c = *j;
  2031. X            *j++ = *i;
  2032. X            *i++ = c;
  2033. X        }
  2034. X    }
  2035. X    /*
  2036. X     * With our sentinel in place, we now run the following hyper-fast
  2037. X     * insertion sort.  For each remaining element, min, from [1] to [n-1],
  2038. X     * set hi to the index of the element AFTER which this one goes.
  2039. X     * Then, do the standard insertion sort shift on a character at a time
  2040. X     * basis for each element in the frob.
  2041. X     */
  2042. X    for (min = base; (hi = min += qsz) < max; ) {
  2043. X        while (qcmp(hi -= qsz, min) > 0)
  2044. X            /* void */;
  2045. X        if ((hi += qsz) != min) {
  2046. X            for (lo = min + qsz; --lo >= min; ) {
  2047. X                c = *lo;
  2048. X                for (i = j = lo; (j -= qsz) >= hi; i = j)
  2049. X                    *i = *j;
  2050. X                *i = c;
  2051. X            }
  2052. X        }
  2053. X    }
  2054. X    return;
  2055. X}
  2056. END_OF_FILE
  2057. if test 6230 -ne `wc -c <'qsort.c'`; then
  2058.     echo shar: \"'qsort.c'\" unpacked with wrong size!
  2059. fi
  2060. # end of 'qsort.c'
  2061. fi
  2062. echo shar: End of archive 1 \(of 2\).
  2063. cp /dev/null ark1isdone
  2064. MISSING=""
  2065. for I in 1 2 ; do
  2066.     if test ! -f ark${I}isdone ; then
  2067.     MISSING="${MISSING} ${I}"
  2068.     fi
  2069. done
  2070. if test "${MISSING}" = "" ; then
  2071.     echo You have unpacked both archives.
  2072.     rm -f ark[1-9]isdone
  2073. else
  2074.     echo You still need to unpack the following archives:
  2075.     echo "        " ${MISSING}
  2076. fi
  2077. ##  End of shell archive.
  2078. exit 0
  2079.  
  2080.